#!/usr/bin/env python3
# -*- coding:utf-8 -*-

from collections import Iterable
import os
import functools

'''
>>>常用函数
tuple()：将一个对象转化为tuple，Iterator对象就可以
list()：将一个对象转化为list，Iterator对象就可以
type()：返回一个对象的类型
dir()：返回一个对象包含的所有方法
'''

##############基础知识##############

# 输入输出
# name = input()
# print(name)

# list
classmates = ['Michael', 'Bob', 'Tracy']
print(classmates)

# tuple
classmates = ('Michael', 'Bob', 'Tracy')
print(classmates)

# dict
d = {'Michael': 95, 'Bob': 75, 'Tracy': 85}
print(d)

# set
s = set([1, 2, 3])
print(s)

# 默认参数
def fun(x, y):
    pass
def fun(x, y=10):
    pass

# 可变参数：可变参数会自动组装成一个tuple
def fun(x, *args):
    pass

# 关键字参数：可变参数会自动组装成一个dict
def fun(x, **kw):
    pass

# 命名关键字参数：y和z就是命名关键字参数
def fun(x, *, y, z=10):
    pass
# 已经有了一个可变参数，后面跟着的命名关键字参数就不再需要一个特殊分隔符*了
# 这里的city和job是命名关键字参数
def person(name, age, *args, city, job):
    print(name, age, args, city, job)

##############高级特性##############

# 切片
L = ['Michael', 'Sarah', 'Tracy', 'Bob', 'Jack']
print(L[0:3]) # 从0到3取数
print(L[:5]) # 前五个数
print(L[-5:]) # 后五个数
print(L[:5:2]) # 前五个数，每隔两个取一个
print(L[::5]) # 所有数，每5个取一个
print(L[:]) # 相当于复制一个list

# 迭代
d = {'a': 1, 'b': 2, 'c': 3}
# 判断一个对象是否可迭代
print(isinstance(d, Iterable))
# 只迭代key
for k in d:
    print(k)
# 同时迭代key与value，需要使用一个items()函数
for k, v in d.items():
    print(k)
    print(v)
# 想要下标，enumerate函数可以把一个list变成索引-元素对
for i, k in enumerate(d):
    print(i)
    print(k)
# 可迭代对象均支持以上操作：dict，list，set，tuple等

# 列表生成器：List Comprehensions
print([x * x for x in range(1, 11)])
print([x * x for x in range(1, 11) if x % 2 == 0])
print([m + n for m in 'ABC' for n in 'XYZ'])
d = {'x': 'A', 'y': 'B', 'z': 'C' }
print([k + '=' + v for k, v in d.items()])
# 实用：列出当前目录下的所有文件和目录名，可以通过一行代码实现
print([d for d in os.listdir('.')])

# 生成器：generator，类似于迭代器
# 方式一：只要把一个列表生成式的[]改成()，就创建了一个generator
g = (x * x for x in range(10))
for i in g:
    print(i)

# 方式二：在函数中加上yield，然后每次通过next()函数调用该函数，也可以使用for迭代
def test():
    l = [x for x in range(100)]
    for i in l:
        yield i
t = test()
print(next(t))

# 迭代器
# 凡是可作用于for循环的对象都是Iterable类型；(可迭代对象)
# 凡是可作用于next()函数的对象都是Iterator类型，它们表示一个惰性计算的序列；（迭代器）
# 集合数据类型如list、dict、str等是Iterable但不是Iterator，不过可以通过iter()函数获得一个Iterator对象。
# Python的for循环本质上就是通过不断调用next()函数实现的

##############函数高阶##############

# 高阶函数map()
# map()函数接收两个参数，一个是函数，一个是Iterable，map将传入的函数依次作用到序列的每个元素，并把结果作为新的Iterator返回。
# 注意：传入参数是一个函数，而不是执行函数，和一个可迭代对象
#       返回参数是一个迭代器
l = (1, 2, 3, 4, 5, 6, 7, 8, 9)
def fun(x):
    return  x * x
m = map(fun, l)
l = tuple(m)
print(l)

# 高阶函数reduce()
# reduce()函数接收两个参数，一个是函数，一个是Iterable
# reduce把一个函数作用在一个序列[x1, x2, x3, ...]上，这个函数必须接收两个参数，reduce把结果继续和序列的下一个元素做累积计算
l = (1, 2, 3, 4, 5, 6, 7, 8, 9)
def fun(a, b):
    return  a * b
r = functools.reduce(fun, l)
print('l的阶乘为：%s 哈哈 %s' % (r, r))

# 高阶函数filter()
# filter()也接收一个函数和一个序列。和map()不同的是，filter()把传入的函数依次作用于每个元素，然后根据返回值是True还是False决定保留还是丢弃该元素。
def is_odd(n):
    return n % 2 == 1
print(list(filter(is_odd, [1, 2, 4, 5, 6, 9, 10, 15])))

# 高阶函数sorted()
# Python内置的sorted()函数就可以对list进行排序
# 它还可以接收一个key函数来实现自定义的排序
# 要进行反向排序，不必改动key函数，可以传入第三个参数reverse=True
l = [5, 9, -12, -21, 36]
print(sorted(l))
print(sorted(l, reverse=True))
print(sorted(l, key=abs, reverse=True))

# 函数可以作为返回值
def fun(x, y):
    def temp():
        return x + y
    return  temp
f = fun(1, 2)
print(f)
print(f())
# 注意：返回函数不要引用任何循环变量，或者后续会发生变化的变量

# 匿名函数：lambda
lam = lambda x: x * x
def f(x):
    return x * x
# 这里的lam等同于函数f

# 装饰器
# 一个简单的例子：给函数打印日志
def log(func):
    def wrapper(*args, **kw):
        print('call %s():' % func.__name__)
        return func(*args, **kw)
    return wrapper
@log
def now():
    return 0
now()
# 注意：这里的wrapper实际上是新的now函数，执行now()实际上执行的是wrapper()
# @log相当于log(now)
# 注意：还需要使用Python内置的functools.wraps，把原始函数的__name__等属性复制到wrapper()函数中
# 完整的写法如下
def log(func):
    @functools.wraps(func) # 必须带上
    def wrapper(*args, **kw):
        print('call %s():' % func.__name__)
        return func(*args, **kw)
    return wrapper
def log(text):
    def decorator(func):
        @functools.wraps(func) # 必须带上
        def wrapper(*args, **kw):
            print('%s %s():' % (text, func.__name__))
            return func(*args, **kw)
        return wrapper
    return decorator

# 偏函数：functools.partial的作用就是，把一个函数的某些参数给固定住（也就是设置默认值），返回一个新的函数，调用这个新函数会更简单
# 偏函数应用于命名关键字参数
def fun(x, *, y):
    return  x + y
test = functools.partial(fun, y=10)
print(fun(10, y=10))
print(test(10))

##############面向对象##############

# python可以始终动态的给实例或者是类定义属性或者是方法

# 访问限制
# 在Python中，实例的变量名如果以__开头，就变成了一个私有变量（private），只有内部可以访问，外部不能访问
class Stunent(object):
    def __init__(self, name):
        self.__name = name
    def show(self):
        print('my name is %s' % self.__name)
# 这样，外部就访问不到__name这个变量了

# 继承与多态
class Animal(object):
    def run(self):
        print('Animal is running...')
class Cat(Animal):
    pass
class Dog(Animal):
    pass
# python支持多继承
class pet(Cat, Dog):
    pass

# 实例属性与类属性
# 这里的A是实例属性，B是类属性
class Test(object):
    def __init__(self, A, B):
        self.A = A
        B = B

# __slots__：限制一个类可以绑定哪些属性
class Student(object):
    __slots__ = ('name', 'age') # 用tuple定义允许绑定的属性名称
s = Student() # 创建新的实例
# s.name = 'Michael' # 绑定属性'name'，pass
# s.age = 25 # 绑定属性'age'，pass
# s.score = 99 # 绑定属性'score'，失败

# @property装饰器：负责把一个方法变成属性调用
class Student(object):
    # 把一个getter方法变成属性，只需要加上@property就可以了
    @property
    def score(self):
        return self._score
    # 此时，@property本身又创建了另一个装饰器@score.setter，负责把一个setter方法变成属性赋值
    @score.setter
    def score(self, value):
        if not isinstance(value, int):
            raise ValueError('score must be an integer!')
        if value < 0 or value > 100:
            raise ValueError('score must between 0 ~ 100!')
        self._score = value

# 定制类
# __len__()：略
# __str__()：略
# __repr__()：通常__repr__ = __str__
# __iter__()：
# __next__()：
# __getitem__()：
# __getattr__()：
# __call__()：
# ()：
# ()：


##############异常处理##############

##############IO处理##############

##############进程线程##############